package com.ls.fw.data.search.impl.es.utils;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class GenericsUtils {
	
	/**
	   * Get the underlying class for a type, or null if the type is a variable type.
	   * @param type the type
	   * @return the underlying class
	   */
	  public static Class<?> getClass(Type type) {
	    if (type instanceof Class) {
	      return (Class) type;
	    }
	    else if (type instanceof ParameterizedType) {
	      return getClass(((ParameterizedType) type).getRawType());
	    }
	    else if (type instanceof GenericArrayType) {
	      Type componentType = ((GenericArrayType) type).getGenericComponentType();
	      Class<?> componentClass = getClass(componentType);
	      if (componentClass != null ) {
	        return Array.newInstance(componentClass, 0).getClass();
	      }
	      else {
	        return null;
	      }
	    }
	    else {
	      return null;
	    }
	  }
	  
	public static <T> List<Class<?>> getTypeArguments(
		    Class<T> baseClass, Class<? extends T> childClass) {
		    Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
		    Type type = childClass;
		    // start walking up the inheritance hierarchy until we hit baseClass
		    while (! getClass(type).equals(baseClass)) {
		      if (type instanceof Class) {
		        // there is no useful information for us in raw types, so just keep going.
		        type = ((Class) type).getGenericSuperclass();
		      }
		      else {
		        ParameterizedType parameterizedType = (ParameterizedType) type;
		        Class<?> rawType = (Class) parameterizedType.getRawType();
		  
		        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
		        TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
		        for (int i = 0; i < actualTypeArguments.length; i++) {
		          resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
		        }
		  
		        if (!rawType.equals(baseClass)) {
		          type = rawType.getGenericSuperclass();
		        }
		      }
		    }
		  
		    // finally, for each actual type argument provided to baseClass, determine (if possible)
		    // the raw class for that type argument.
		    Type[] actualTypeArguments;
		    if (type instanceof Class) {
		      actualTypeArguments = ((Class) type).getTypeParameters();
		    }
		    else {
		      actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
		    }
		    List<Class<?>> typeArgumentsAsClasses = new ArrayList<Class<?>>();
		    // resolve types by chasing down type variables.
		    for (Type baseType: actualTypeArguments) {
		      while (resolvedTypes.containsKey(baseType)) {
		        baseType = resolvedTypes.get(baseType);
		      }
		      typeArgumentsAsClasses.add(getClass(baseType));
		    }
		    return typeArgumentsAsClasses;
		  }
	
	
	public static Class getSuperClassGenericType(Class clazz, int index) {
		Type genType = clazz.getGenericSuperclass();// 得到泛型父类
		// 如果没有实现ParameterizedType接口，即不支持泛型，直接返回Object.class
		if (!(genType instanceof ParameterizedType)) {
			return Object.class;
		}
		// 返回表示此类型实际类型参数的Type对象的数组,数组里放的都是对应类型的Class, 如BuyerServiceBean extends
		// DaoSupport就返回Buyer和Contact类型
		Type[] parameters = ((ParameterizedType) genType)
				.getActualTypeArguments();
		if (index > parameters.length || index < 0) {
			throw new RuntimeException("你输入的索引号"
					+ (index < 0 ? "不能小于0" : "超出了参数的总数"));
		}
		if (!(parameters[index] instanceof Class)) {
			return Object.class;
		}
		return (Class) parameters[index];
	}

	@SuppressWarnings("unchecked")
	public static Class getSuperClassGenericType(Class clazz) {
		{
		}
		return getSuperClassGenericType(clazz, 0);
	}

	@SuppressWarnings("unchecked")
	public static Class getMethodGenericReturnType(Method method, int index) {
		Type returnType = method.getGenericReturnType();
		if (returnType instanceof ParameterizedType) {
			ParameterizedType type = (ParameterizedType) returnType;
			Type[] typeArguments = type.getActualTypeArguments();
			if (index >= typeArguments.length || index < 0) {
				throw new RuntimeException("你输入的索引"
						+ (index < 0 ? "不能小于0" : "超出了参数的总数"));
			}
			return (Class) typeArguments[index];
		}
		return Object.class;
	}

	@SuppressWarnings("unchecked")
	public static Class getMethodGenericReturnType(Method method) {
		return getMethodGenericReturnType(method, 0);
	}

	@SuppressWarnings("unchecked")
	public static List getMethodGenericParameterTypes(Method method, int index) {
		List results = new ArrayList();
		Type[] genericParameterTypes = method.getGenericParameterTypes();
		if (index > genericParameterTypes.length || index < 0) {
			throw new RuntimeException("你输入的索引"
					+ (index < 0 ? "不能小于0" : "超出了参数的总数"));
		}
		Type genericParamenterType = genericParameterTypes[index];
		if (genericParamenterType instanceof ParameterizedType) {
			ParameterizedType aType = (ParameterizedType) genericParamenterType;
			Type[] parameterArgTypes = aType.getActualTypeArguments();
			for (Type parameterArgType : parameterArgTypes) {
				Class parameterArgClass = (Class) parameterArgType;
				results.add(parameterArgClass);
			}
			return results;
		}
		return results;
	}

	@SuppressWarnings("unchecked")
	public static List getMethodGenericParameterTypes(Method method) {
		return getMethodGenericParameterTypes(method, 0);
	}

	@SuppressWarnings("unchecked")
	public static Class getFieldGenericType(Field field, int index) {
		Type genericFileType = field.getGenericType();
		if (genericFileType instanceof ParameterizedType) {
			ParameterizedType aType = (ParameterizedType) genericFileType;
			Type[] fieldArgTypes = aType.getActualTypeArguments();
			if (index > fieldArgTypes.length || index < 0) {
				throw new RuntimeException("你输入的索引"
						+ (index < 0 ? "不能小于0" : "超出了参数的总数"));
			}
			return (Class) fieldArgTypes[index];
		}
		return Object.class;
	}

	@SuppressWarnings("unchecked")
	public static Class getFieldGenericType(Field field) {
		return getFieldGenericType(field, 0);
	}
}